﻿using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace NewScene
{
    public class WorldIndexBuilder : EditorWindow
    {
        static bool IsIgnored(GameObject go)
        {
            return go.hideFlags == HideFlags.NotEditable ||
                   go.scene.name == null ||
                   go.GetComponent<MeshFilter>() == null;
        }

        struct ObjItem
        {
            public int Id;
            public Bounds Bound;
        }
        
        [MenuItem("Tools/Build World")]
        static void BuildSpace()
        {
            //获取整个世界的边界
            //根据边界进行分块
            //获取所有资源对象，给每个资源建立唯一索引
                 //根据资源在哪个场景，把资源索引分配给场景序列化保持
                 //记录这个资源有哪些lod，对应lod有哪些mesh，mat，建立对应的数据记录这些mesh和mat对应的路径信息 然后写入资源索引
            //根据包围盒分析每个资源归属哪个块，如果这个资源有对应lod信息，把lod信息写入box中
            
            Assets = new List<SpaceAsset>();
            DataRefs = new List<SpaceAssetDataRef>();
            SpaceBoxes = new List<SpaceBox>();

            var count = UnityEngine.SceneManagement.SceneManager.loadedSceneCount;
            int sceneObjIndex = 0;
            for(int i = 0;i<count;i++)
            {
                var scene = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i);
                var config = new GameObject("_SceneConfig").AddComponent<GameSceneConfig>();
                UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(config.gameObject, scene);
                var result = BuildScene(scene);
                foreach (var item in result.Item1)
                {
                    Assets.Add(new SpaceAsset()
                        { SceneObjKey = sceneObjIndex, AssetObjKey = sceneObjIndex, Bound = item.Bound });
                    sceneObjIndex++;

                    DataRefs.Add(new SpaceAssetDataRef()
                    {
                        AssetObjKey = sceneObjIndex,
                        Lod0AllAssets = result.Item2[0].ToArray(),
                        Lod1AllAssets = result.Item2[1].ToArray(),
                        Lod2AllAssets = result.Item2[2].ToArray()
                    });

                    var spaceRef = new SpaceAssetRef() { SceneObjKey = sceneObjIndex};
                    if (result.Item3.TryGetValue(item.Id, out var listFilter))
                    {
                        spaceRef.Lod0Filter = listFilter[0].ToArray();
                        spaceRef.Lod1Filter = listFilter[1].ToArray();
                        spaceRef.Lod2Filter = listFilter[2].ToArray();
                    }
                    if (result.Item4.TryGetValue(item.Id, out var listRender))
                    {
                        spaceRef.Lod0Renderer = listRender[0].ToArray();
                        spaceRef.Lod1Renderer = listRender[1].ToArray();
                        spaceRef.Lod2Renderer = listRender[2].ToArray();
                    }
                    config.AssetsInScene.Add(spaceRef);
                }
            }
            Debug.Log($"✅ 已加载 {Assets.Count} 个资源索引");
            
            bool hasBounds = false;
            Bounds totalBounds = new Bounds();
            Terrain[] terrains = GameObject.FindObjectsOfType<Terrain>(false);
            foreach (var terrain in terrains)
            {
                // 将 Terrain 的局部包围盒转换为世界空间
                Bounds worldBounds = new Bounds(
                    terrain.transform.position + terrain.terrainData.bounds.center,
                    terrain.terrainData.bounds.size
                );

                if (!hasBounds)
                {
                    totalBounds = worldBounds;
                    hasBounds = true;
                }
                else
                {
                    totalBounds.Encapsulate(worldBounds);
                }
            }
            Debug.Log($"场景边界：{totalBounds}");
            
            //根据边界的长宽度进行分块
            var x1 = Mathf.FloorToInt(totalBounds.min.x / SpaceBox.SpaceBoxSize);
            var x2 = Mathf.FloorToInt(totalBounds.max.x / SpaceBox.SpaceBoxSize);
            var z1 = Mathf.FloorToInt(totalBounds.min.z / SpaceBox.SpaceBoxSize);
            var z2 = Mathf.FloorToInt(totalBounds.max.z / SpaceBox.SpaceBoxSize);
            var column = x2 - x1;
            var row = z2 - z1;
            for (int i = 0; i < row; i++)
            {
                for (int col = 0; col < column; col++)
                {
                    SpaceBoxes.Add(new SpaceBox()
                    {
                        BoxId = i * column + col,
                        Center = new Vector2Int(x1 + col * SpaceBox.SpaceBoxSize, z1 + i * SpaceBox.SpaceBoxSize),
                        Weight = 0,
                    });
                }
            }
            Debug.Log($"✅ 已生成 {row} 行 {column} 列的 SpaceBox");

            var rect = new Rect(x1, z1, column * SpaceBox.SpaceBoxSize, row * SpaceBox.SpaceBoxSize);
            foreach (var item in Assets)
            {
                var ids = GetBoxes(item.Bound, rect, SpaceBox.SpaceBoxSize);
                foreach (var idx in ids)
                {
                    _MarkAssetToBox(idx, item.SceneObjKey);
                }
            }

            AssetDatabase.SaveAssets();

            void _MarkAssetToBox(int boxId, int sceneObjKey)
            {
                var ss = DataRefs[sceneObjKey].Lod0AllAssets;
                if (ss != null && ss.Length > 0)
                {
                    SpaceBoxes[boxId].Lod0.Add(sceneObjKey);  //这个资源存在这个box 中
                }
                ss = DataRefs[sceneObjKey].Lod1AllAssets;
                if (ss != null && ss.Length > 0)
                {
                    SpaceBoxes[boxId].Lod1.Add(sceneObjKey);  //这个资源存在这个box 中
                }
                ss = DataRefs[sceneObjKey].Lod2AllAssets;
                if (ss != null && ss.Length > 0)
                {
                    SpaceBoxes[boxId].Lod2.Add(sceneObjKey);
                }
            }
        }

        private static List<int> GetBoxes(Bounds bound,Rect world,  int boxSize)
        {
            List<int> boxIndices = new List<int>();

            // 获取世界坐标的最小行列索引
            int startX = Mathf.FloorToInt(world.xMin / boxSize);
            int endX = Mathf.FloorToInt(world.xMax / boxSize);
            int startZ = Mathf.FloorToInt(world.yMin / boxSize);
            int endZ = Mathf.FloorToInt(world.yMax / boxSize);

            // 计算列数
            int column = (endX - startX) + 1;

            // 计算包围盒在二维空间中的范围
            int minX = Mathf.FloorToInt((bound.min.x - world.xMin) / boxSize);
            int maxX = Mathf.FloorToInt((bound.max.x - world.xMin) / boxSize);
            int minZ = Mathf.FloorToInt((bound.min.z - world.yMin) / boxSize);
            int maxZ = Mathf.FloorToInt((bound.max.z - world.yMin) / boxSize);

            // 遍历所有可能覆盖的 Box
            for (int z = minZ; z <= maxZ; z++)
            {
                for (int x = minX; x <= maxX; x++)
                {
                    // 确保 x 和 z 在有效范围内
                    if (x >= 0 && x < column && z >= 0 && z < (endZ - startZ + 1))
                    {
                        int boxId = z * column + x;
                        boxIndices.Add(boxId);
                    }
                }
            }

            return boxIndices;
        }

        private static (List<ObjItem>, Dictionary<int, List<string>>, Dictionary<int, List<List<MeshFilter>>>,
            Dictionary<int, List<List<MeshRenderer>>>) BuildScene(UnityEngine.SceneManagement.Scene scene)
        {
            List<ObjItem> sceneObjInstanceIds = new List<ObjItem>(3000);
            //List<>
            Dictionary<int, List<string>> sceneObjAssetIds = new Dictionary<int, List<string>>();
            Dictionary<int, List<List<MeshFilter>>> sceneObjFilters = new Dictionary<int, List<List<MeshFilter>>>();
            Dictionary<int, List<List<MeshRenderer>>> sceneObjRenders = new Dictionary<int, List<List<MeshRenderer>>>();

            void _Regist(int id, Transform lod, int index)
            {
                var ff = lod.GetComponentsInChildren<MeshFilter>();
                sceneObjFilters[id][index].AddRange(ff);
                var rr = lod.GetComponentsInChildren<MeshRenderer>();
                sceneObjRenders[id][index].AddRange(rr);
                foreach (var f in ff)
                {
                    var path = AssetDatabase.GetAssetPath(f.sharedMesh);
                    sceneObjAssetIds[id].Add(string.IsNullOrEmpty(path) ? "EMPTY" : path);
                }

                foreach (var r in rr)
                {
                    for (int i = 0; i < r.materials.Length; i++)
                    {
                        var path = AssetDatabase.GetAssetPath(r.materials[i]);
                        sceneObjAssetIds[id].Add(string.IsNullOrEmpty(path) ? "EMPTY" : path);
                    }
                }
            }

            Bounds _Bound(GameObject go)
            {
                var filters = go.GetComponentsInChildren<MeshFilter>();
                Bounds bounds = new Bounds();
                bool hasBounds = false;
                foreach (var filter in filters)
                {
                    if (filter.sharedMesh != null)
                    {
                        if (!hasBounds)
                        {
                            bounds = filter.sharedMesh.bounds;
                            hasBounds = true;
                        }
                        else
                        {
                            bounds.Encapsulate(filter.sharedMesh.bounds);
                        }
                    }
                }

                return bounds;
            }

            var allObjects = scene.GetRootGameObjects();
            foreach (var go in allObjects)
            {
                if (IsIgnored(go)) continue;

                var id = go.GetInstanceID();
                sceneObjInstanceIds.Add(new ObjItem()
                {
                    Id = id,
                    Bound = _Bound(go)
                });
                sceneObjAssetIds.Add(id, new List<string>());
                sceneObjFilters.Add(id, new List<List<MeshFilter>>()
                {
                    new List<MeshFilter>(),
                    new List<MeshFilter>(),
                    new List<MeshFilter>()
                });
                sceneObjRenders.Add(id, new List<List<MeshRenderer>>()
                {
                    new List<MeshRenderer>(),
                    new List<MeshRenderer>(),
                    new List<MeshRenderer>()
                });

                var lod0 = go.transform.Find("LOD0");
                if (lod0 != null)
                {
                    _Regist(id, lod0, 0);
                }

                var lod1 = go.transform.Find("LOD1");
                if (lod1 != null)
                {
                    _Regist(id, lod1, 1);
                }

                var lod2 = go.transform.Find("LOD2");
                if (lod2 != null)
                {
                    _Regist(id, lod2, 2);
                }
            }
            
            Debug.Log($"✅ 已加载 {sceneObjInstanceIds.Count} 个物体索引");

            return (sceneObjInstanceIds, sceneObjAssetIds, sceneObjFilters, sceneObjRenders);
        }

        private static List<SpaceAsset> Assets;  //所有资源
        private static List<SpaceAssetDataRef> DataRefs;   //所有资源数据
        private static List<SpaceBox> SpaceBoxes;    //所有空间块
    }
}